#include "dswlocalsndplay.h"

const char* SND_LIST[] = {SND_LOCK, SND_UNLOCK, SND_TRIG};

CDswlocalsndplay::CDswlocalsndplay(LPST_ENV_T pEnv)
: m_pEnv(pEnv)
, m_thread_id(NULL)
{
	LOG4CPLUS_DEBUG(log_dsw, "Create local snd play!");

	m_pPCMPacket = new char[A_BITSTREAM_LEN];
	pthread_mutex_init(&m_nLock, NULL);
	pthread_create(&m_thread_id, NULL, CDswlocalsndplay::cbLocalSndPlayThread, (void*)this);
}

CDswlocalsndplay::~CDswlocalsndplay()
{
	if (m_thread_id)
		pthread_join(m_thread_id, NULL);

	while(!m_nPlayTypeList.empty()){
		m_nPlayTypeList.pop_front();
	}
	pthread_mutex_destroy(&m_nLock);

	delete m_pPCMPacket;
	LOG4CPLUS_DEBUG(log_dsw, "Destroy local snd play!");
}

void CDswlocalsndplay::AppendPlayTypeNode(int nType, int nValue)
{
	pthread_mutex_lock(&m_nLock);
	if (nType == TLV_T_SET_GARRISON_WITHDRAW){
		if (nValue == LOCK_DISABLE)
			m_nPlayTypeList.push_back(PLAY_SND_UNLOCK);
		else if ((nValue == LOCK_ENABLE) || (nValue == LOCK_OUTSIDE))
			m_nPlayTypeList.push_back(PLAY_SND_LOCK);
	}
	else if (nType == TLV_T_SET_ARE_ALARM)
		m_nPlayTypeList.push_back(PLAY_SND_TRIG);		
	pthread_mutex_unlock(&m_nLock);
}

void CDswlocalsndplay::LocalSndPlayThread()
{
	LOG4CPLUS_DEBUG(log_dsw, "Start play local snd thread!");
	int nMsg = -1;

	while(!m_pEnv->bExit){
		pthread_mutex_lock(&m_nLock);
		if (!m_nPlayTypeList.empty()){
			nMsg = m_nPlayTypeList.front();
			m_nPlayTypeList.pop_front();
		}
		pthread_mutex_unlock(&m_nLock);
		if (nMsg == -1){
			usleep(1000 * 100);
			continue;
		}
//		PlayLocalSnd(SND_LIST[nMsg]);
		PlayLocalSnd_fd(SND_LIST[nMsg]);
		nMsg = -1;
	}
	LOG4CPLUS_DEBUG(log_dsw, "Exit play local snd thread!");
}

typedef struct{
	char riff_id[4];
	long size0;
	char wave_fmt[8];
	long size1;
	short fmttag;
	short channel;
	long samplespersec;
	long bytepersec;
	short blockalign;
	short bitpersamples;
	char data_id[4];
	long size2;
} __attribute__ ((packed)) ST_WAV_T, *LPST_WAV_T;

void CDswlocalsndplay::PlayLocalSnd_fd(const char* pFileName)
{
	FILE* fp = fopen(pFileName, "rb");
	if (fp == NULL){
		LOG4CPLUS_ERROR(log_dsw, "Play " << pFileName << " failed!");
		return;
	}

	LPST_AUDIO_FRAME_T pFrame;
	void* pParam;
	pFrame = (LPST_AUDIO_FRAME_T)m_pPCMPacket;
	pParam = (void*)pFrame;

	pFrame->bzEncType = STREAM_ENCTYPE_AUDIO_PCM16X8K;
	pFrame->dwBufferType = 0;
	pFrame->dwFrameNumber = 0;
	pFrame->dwSec = 0;
	pFrame->dwUsec = 0;
	pFrame->dwFrameSize = 256;
	while(!m_pEnv->bExit){
		memset(&(pFrame->pAudioBuffer), 0, 256);
		int nLen = fread(&(pFrame->pAudioBuffer), 1, 256, fp);
		if (nLen > 0){
			m_pEnv->pOnNotifyFunc(PMSG_PUT_AUDIOFRAME, pParam);
			usleep(1000 * 10);
		}
		else
			break;
	}
	fclose(fp);
	LOG4CPLUS_DEBUG(log_dsw, "Play " << pFileName);
}

void CDswlocalsndplay::PlayLocalSnd(const char* pFileName)
{
	LOG4CPLUS_DEBUG(log_dsw, "Play " << pFileName);

	AVFormatContext* context = avformat_alloc_context();
	int audio_stream_index;
	bool bEnd = false;	

	context->interrupt_callback.callback = CDswlocalsndplay::cbInterruptStream;
	context->interrupt_callback.opaque = this;

	if (avformat_open_input(&context, pFileName, NULL, NULL) != 0){
		LOG4CPLUS_DEBUG(log_dsw, "avformat_open_input failed! file = " << pFileName);
		goto END_PLAY;
	}

	if (avformat_find_stream_info(context, NULL) < 0){
		LOG4CPLUS_DEBUG(log_dsw, "avformat_find_stream_info failed! file = " << pFileName);
		goto END_PLAY;
	}

	for(int i = 0; i < context->nb_streams; i++){
		if(context->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
			audio_stream_index = i;
			break;
		}
	}

	av_dump_format(context, 0, context->filename, 0);

	AVCodecContext* codecCxt;
	codecCxt = context->streams[audio_stream_index]->codec;
	AVCodec* codec;
	codec = avcodec_find_decoder(codecCxt->codec_id);

	if (avcodec_open2(codecCxt, codec, NULL) < 0){
		LOG4CPLUS_DEBUG(log_dsw, "avcodec_open2 failed! file = " << pFileName);
		goto END_PLAY;
	}

	AVPacket packet;
	AVFrame nPCMFrame;
	int got_frame, data_size;
	av_init_packet(&packet);
	LPST_AUDIO_FRAME_T pFrame;
	void* pParam;
	char* pPos;
	pFrame = (LPST_AUDIO_FRAME_T)m_pPCMPacket;
	pParam = (void*)pFrame;

	pFrame->bzEncType = STREAM_ENCTYPE_AUDIO_PCM16X8K;
	pFrame->dwBufferType = 0;
	pFrame->dwFrameNumber = 0;
	pFrame->dwSec = 0;
	pFrame->dwUsec = 0;

	while(!m_pEnv->bExit && !bEnd){
		int av_ret;
		av_ret = av_read_frame(context, &packet);
		if (av_ret >= 0){
			if (packet.stream_index == audio_stream_index){
				avcodec_get_frame_defaults(&nPCMFrame);
				pFrame->dwFrameSize = avcodec_decode_audio4(codecCxt, &nPCMFrame, &got_frame, &packet);
				if (pFrame->dwFrameSize < 0){
					char sErr[512] = {0};
					av_strerror(errno, sErr, sizeof(sErr));
					LOG4CPLUS_ERROR(log_dsw, "avcodec_decode_audio4 failed! stream = " << audio_stream_index);
					bEnd = true;
				}
				else{
					if (got_frame){
						data_size = av_samples_get_buffer_size(NULL, codecCxt->channels, nPCMFrame.nb_samples, codecCxt->sample_fmt, 1);
						pPos = (char*)nPCMFrame.data[0];
						while(data_size > 256){
							pFrame->dwFrameSize = 256;
							memcpy(&(pFrame->pAudioBuffer), pPos, 256);
							m_pEnv->pOnNotifyFunc(PMSG_PUT_AUDIOFRAME, pParam);
							usleep(1000 * 10);
							data_size -= 256;
							pPos += 256;
						}
						if (data_size > 0){
							pFrame->dwFrameSize = 256;
							memset(&(pFrame->pAudioBuffer), 0, 256);
							memcpy(&(pFrame->pAudioBuffer), pPos, data_size);
							m_pEnv->pOnNotifyFunc(PMSG_PUT_AUDIOFRAME, pParam);
							usleep(1000 * 10);
						}
					}
				}
				av_free_packet(&packet);
			}
		}
		else{
			char sErr[512] = {0};
			av_strerror(errno, sErr, sizeof(sErr));
			LOG4CPLUS_ERROR(log_dsw, "Read packet failed! stream = " << audio_stream_index);
			bEnd = true;
		}
//		av_free_packet(&packet);
		usleep(1000 * 10);
	}
	avcodec_close(codecCxt);

END_PLAY:
	avformat_free_context(context);
}

